/**
 * 
 */
package org.statewalker.fsm.providers;

import java.util.List;

import org.statewalker.fsm.IFsmStateProvider;

/**
 * @author kotelnikov
 */
public abstract class AbstractFsmStateProvider<SK, S, EK, E>
    implements
    IFsmStateProvider<S, E>,
    IFsmConfigurator<SK, EK> {

    protected EK fDefaultEventKey;

    protected SK fFinalStateKey;

    protected SK fInitialStateKey;

    /**
 * 
 */
    public AbstractFsmStateProvider(
        SK initialStateKey,
        SK finalStateKey,
        EK defaultEventKey) {
        fInitialStateKey = initialStateKey;
        fFinalStateKey = finalStateKey;
        fDefaultEventKey = defaultEventKey;
    }

    protected abstract EK getEventKey(E event);

    /**
     * @see org.statewalker.fsm.IFsmStateProvider#getNextState(List<S>,
     *      java.lang.Object, java.lang.Object)
     */
    public final S getNextState(List<S> stack, S state, final E event) {
        if (stack.isEmpty())
            return null;
        SK stateKey = getStateKey(state);
        if (stateKey == null)
            stateKey = fInitialStateKey;
        SK targetKey = null;
        EK eventKey = getEventKey(event);
        S parent = stack.get(stack.size() - 1);
        while (eventKey != null) {
            targetKey = getNextSubstateKey(parent, stateKey, eventKey);
            if (targetKey != null)
                break;
            eventKey = getParentEventKey(eventKey);
        }
        if (targetKey == null || fFinalStateKey.equals(targetKey))
            return null;
        return newState(stack, targetKey);
    }

    protected abstract SK getNextSubstateKey(S parent, SK stateKey, EK eventKey);

    protected EK getParentEventKey(EK eventKey) {
        if (fDefaultEventKey.equals(eventKey))
            return null;
        return fDefaultEventKey;
    }

    protected abstract SK getStateKey(S state);

    protected abstract S newState(List<S> stack, SK targetKey);

}
